package br.com.atibrasil.dao;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;

import br.com.atibrasil.util.GEEPPException;

public class DAOImpl<T> implements Serializable {

	private static final long serialVersionUID = 1L;
	private static Connection conn;
	private static String driver;
	private static String url;
	private static String user;
	private static  String password;		
	protected T current;

	public DAOImpl() {
		this.emf = Persistence.createEntityManagerFactory("ATIBRASIL");
		getProperties();
	}

	public DAOImpl(String unit) {
		this.emf = Persistence.createEntityManagerFactory(unit);
		getProperties();		
	}

	protected EntityManagerFactory emf = null;

	public EntityManager getEntityManager() {
		return emf.createEntityManager();
	}

	public T find(Object id) {
		EntityManager em = getEntityManager();
		T object;
		try {
			object = (T) em.find(current.getClass(), id);
			if(object!=null){
				em.refresh(object);
			}
			return object;
		} finally {
			em.close();
		}
	}

	public List<T> findAll() {
		EntityManager em = getEntityManager();
		try {
			CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
			cq.select(cq.from(current.getClass()));
			cq.orderBy(new ArrayList());
			Query q = em.createQuery(cq);
			return q.getResultList();
		} finally {
			em.close();
		}
	}

	public T getCurrent() {
		return current;
	}

	public void setCurrent(T current) {
		this.current = current;
	}

	public T persist(T object) throws Exception {
		EntityManager em = null;
		try {
			em = getEntityManager();
			em.getTransaction().begin();
			object = em.merge(object);
			em.getTransaction().commit();
		} catch (Exception ex) {
			ex.printStackTrace();
			if( em.getTransaction().isActive() ){
				em.getTransaction().rollback();				
			}
			throw ex;
		} finally {
			if (em != null) {
				em.close();
			}
		}
		return object;
	}
	
	public List<T> persist(List<T> object) throws Exception {
		EntityManager em = null;
		try {
			em = getEntityManager();
			em.getTransaction().begin();
			for (Iterator<T> iterator = object.iterator(); iterator.hasNext();) {
				em.merge(iterator.next());
			}
			em.getTransaction().commit();
		} catch (Exception ex) {
			em.getTransaction().rollback();
			throw ex;
		} finally {
			if (em != null) {
				em.close();
			}
		}
		return object;
	}

	public void delete(int object) throws GEEPPException {
		EntityManager em = null;
		try {
			em = getEntityManager();
			Object o = em.find(current.getClass(), object);
			em.getTransaction().begin();
			em.remove(o);
			em.getTransaction().commit();
		} catch (Exception ex) {
//			em.getTransaction().rollback();
			throw new GEEPPException(ex);
		} finally {
			if (em != null) {
				em.close();
			}
		}
	}
	
	public void delete(List<T> object) throws Exception {
		EntityManager em = null;
		try {
			em = getEntityManager();
			em.getTransaction().begin();
			for (T o : object) {						
				o = em.merge(o);			
				em.remove(o);
			}
			em.getTransaction().commit();
		} catch (Exception ex) {
			em.getTransaction().rollback();
			throw ex;
		} finally {
			if (em != null) {
				em.close();
			}
		}
	}	

	public void persist(T[] object) throws Exception {

	}
	
	public void validate(T[] object) throws Exception {

	}	

	public List<T> findByNamedQuery(String namedQuery, Object... parameter)
			throws Exception {
		EntityManager em = getEntityManager();
		try {
			Query q = em.createNamedQuery(namedQuery);
			for (int i = 0; i < parameter.length; i++) {
				q.setParameter(i + 1, parameter[i]);
			}
			return q.getResultList();
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally {
			em.close();
		}

	}
	
	
	public List<Object[]> findByNativeQuery(String nativeQuery)
			throws Exception {
		EntityManager em = getEntityManager();
		try {
			Query q = em.createNativeQuery(nativeQuery);
			return q.getResultList();
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally {
			em.close();
		}
	}	
	
	public Object findBySingleNativeQuery(String nativeQuery)
			throws Exception {
		EntityManager em = getEntityManager();
		try {
			Query q = em.createNativeQuery(nativeQuery);
			return q.getSingleResult();
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally {
			em.close();
		}
	}		
	
	

	public int executeByNamedQuery(String namedQuery, Object... parameter)
			throws Exception {
		EntityManager em = getEntityManager();
		em.getTransaction().begin();
		try {
			Query q = em.createNamedQuery(namedQuery);
			for (int i = 0; i < parameter.length; i++) {
				q.setParameter(i + 1, parameter[i]);
			}
			int result = q.executeUpdate();
			em.getTransaction().commit();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally {
			em.close();
		}

	}

	public T findSingleByNamedQuery(String namedQuery, Object... parameter)
			throws Exception {
		EntityManager em = getEntityManager();
		try {
			Query q = em.createNamedQuery(namedQuery);
			for (int i = 0; i < parameter.length; i++) {
				q.setParameter(i + 1, parameter[i]);
			}
			return (T) q.getSingleResult();
		} finally {
			em.close();
		}

	}

	public Object findGroups(String namedQuery, Object... parameter)
			throws Exception {
		EntityManager em = getEntityManager();
		try {
			Query q = em.createNamedQuery(namedQuery);
			for (int i = 0; i < parameter.length; i++) {
				q.setParameter(i + 1, parameter[i]);
			}
			return q.getSingleResult();

		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		} finally {
			em.close();
		}

	}
	
	public Object findGenericByNamedQuery(String namedQuery, Object... parameter)
			throws Exception {
		EntityManager em = getEntityManager();
		try {
			Query q = em.createNamedQuery(namedQuery);
			for (int i = 0; i < parameter.length; i++) {
				q.setParameter(i + 1, parameter[i]);
			}
			return q.getResultList();

		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		} finally {
			em.close();
		}			
	}
	
	private void getProperties() {
		this.driver = getProperty("javax.persistence.jdbc.driver");		
		this.url = getProperty("javax.persistence.jdbc.url");
		this.user = getProperty("javax.persistence.jdbc.user");
		this.password = getProperty("javax.persistence.jdbc.password");				
	}	
	
	public String getProperty(String nome) {
		return (String) getEntityManager().getProperties().get(nome);
	}

	public static Connection getConnection() {
 		try {
			Class.forName(driver); 			
			return DriverManager.getConnection(url, user, password);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return conn;
	}	
}
